package drds.global_transaction.image;

import drds.common.Author;
import drds.dbutils.native_sql.Db;
import drds.dbutils.tools.DbUtils;
import drds.global_transaction.Authors;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.sql.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Author(name = Authors.LI_YANG)
@Slf4j
public class UndoLog
{

    @Setter
    @Getter
    private Long id;


    @Setter
    @Getter
    private String originalSql;
    @Setter
    @Getter
    private String type;
    @Setter
    @Getter
    private String primaryKeyColumnName;
    @Setter
    @Getter
    private String selectBody;
    @Setter
    @Getter
    private String selectWhere;
    //
    @Setter
    @Getter
    private Image beforeImage;
    @Setter
    @Getter
    private Image afterImage;


    public boolean isInsert()
    {
        return "insert".equalsIgnoreCase(getType());
    }

    public boolean isUpdate()
    {
        return "update".equalsIgnoreCase(getType());
    }

    public boolean isDelete()
    {
        return "delete".equalsIgnoreCase(getType());
    }

    public void rollback() throws SQLException
    {

        log.info("originalSql={}", originalSql);
        try
        {
            if (validAfterImage())
            {
                rollbackBeforeImage();
            } else
            {
                throw new IllegalStateException();
            }
        } catch (SQLException e)
        {
            log.error("rollback.SQLException={}", e);
            throw e;
        }
    }

    private boolean validAfterImage() throws SQLException
    {

        if (beforeImage == null || afterImage == null || StringUtils.isEmpty(primaryKeyColumnName) || StringUtils.isEmpty(originalSql)
                || StringUtils.isEmpty(type) || StringUtils.isEmpty(selectBody) || StringUtils
                .isEmpty(selectWhere))
        {
            log.error("validImageParameterNUll");
            throw new SQLException("validImageParameterNUll");
        }
        if (isInsert() || isUpdate())
        {
            List<Record> recordList = afterImage.getRecordList();
            //找出所有的后像记录，查看和当前记录是否一直，如果不一致，报错人工处理

            for (Record record : recordList)
            {
                List<Column> columnList = record.getColumnList();
                Map<String, Object> map = new HashMap<String, Object>();
                for (Column column : columnList)
                {
                    map.put(column.getName(), column.getValue());
                }
                String primaryKeyColumnCondition = "";
                Object primaryKeyColumnValue = map.get(primaryKeyColumnName);
                if (primaryKeyColumnValue == null)
                {
                    throw new SQLException("ValidAfterImage.Unsupport null premaryKey");
                }
                if (primaryKeyColumnValue instanceof String)
                {
                    primaryKeyColumnCondition = "  " + primaryKeyColumnName + "='" + primaryKeyColumnValue + "'";
                } else
                {
                    primaryKeyColumnCondition = "  " + primaryKeyColumnName + "=" + primaryKeyColumnValue;
                }
                String selectSql;
                if (getSelectWhere().toLowerCase().trim().endsWith("and"))
                {
                    selectSql = getSelectBody() + getSelectWhere() + primaryKeyColumnCondition;
                } else
                {
                    selectSql = getSelectBody() + getSelectWhere() + " and " + primaryKeyColumnCondition;
                }
                ResultSet resultSet = Db.query(null, selectSql);
                boolean ok = false;
                while (resultSet.next())
                {
                    for (Map.Entry<String, Object> entry : map.entrySet())
                    {
                        Object objcect = resultSet.getObject(entry.getKey());
                        if (objcect instanceof Timestamp)
                            continue;
                        if (objcect instanceof Integer && entry.getValue() instanceof Integer)
                        {
                            return objcect.equals(entry.getValue());
                        }
                        if (!objcect.toString().equals(entry.getValue().toString()))
                        {
                            log.error(
                                    "Rollback sql error,because now resultData is not equals afterImage,change to manual operation!");
                            log.error("nowVal=" + objcect + ",entry.getValue()=" + entry.getValue());

                            return false;
                        }
                    }
                    return true;
                }

                if (!ok)
                    return false;

            }
            return true;
        } else if (isDelete())
        {
            Connection connection = null;
            Statement statement = connection.createStatement();
            ResultSet resultSet = statement.executeQuery(selectBody + selectWhere);
            boolean exist = !resultSet.next();
            DbUtils.close(resultSet);
            DbUtils.close(statement);
            return exist;
        } else
            return false;
    }

    private void rollbackBeforeImage() throws SQLException
    {

        if (isInsert())
        {
            String lastSql = "delete from " + afterImage.getTableName() + " " + selectWhere;
            Db.update(null, lastSql);
        } else if (isUpdate())
        {
            List<Record> recordList = beforeImage.getRecordList();
            for (Record record : recordList)
            {
                List<Column> columnList = record.getColumnList();
                Map<String, Object> map = new HashMap<String, Object>();
                StringBuilder sb = new StringBuilder();
                for (Column column : columnList)
                {
                    map.put(column.getName(), column.getValue());
                    if (column.getValue() instanceof String)
                    {
                        sb.append(" " + column.getName() + "='" + column.getValue() + "',");
                    } else if ("java.sql.Timestamp".equals(column.getType()))
                    {
                        if (column.getValue() instanceof Long)
                        {
                            sb.append(" " + column.getName() + "=" + new Timestamp((long) column.getValue()) + ",");
                        } else if (column.getValue() instanceof Timestamp)
                        {
                            sb.append(" " + column.getName() + "=" + column.getValue() + ",");

                        } else
                        {
                            throw new SQLException("Cannot handle type=" + column.getValue().getClass().getName());
                        }
                    } else
                    {
                        sb.append(" " + column.getName() + "=" + column.getValue() + ",");
                    }
                }
                while (sb.charAt(sb.length() - 1) == ',')
                {
                    sb.deleteCharAt(sb.length() - 1);
                }

                String whereCondition = "";
                Object primaryKeyColumnValue = map.get(primaryKeyColumnName);
                if (primaryKeyColumnValue == null)
                {
                    throw new SQLException("PkVal null");
                }
                if (primaryKeyColumnValue instanceof String)
                {
                    whereCondition = " where " + primaryKeyColumnName + "='" + primaryKeyColumnValue + "'";
                } else
                {
                    whereCondition = " where " + primaryKeyColumnName + "=" + primaryKeyColumnValue;
                }

                String sql = "update " + beforeImage.getTableName() + " set " + sb + whereCondition;
                Db.update(null, sql);

            }
        } else if (isDelete())
        {
            List<Record> recordList = beforeImage.getRecordList();
            for (Record record : recordList)
            {
                List<Column> columnList = record.getColumnList();
                StringBuilder columnNameList = new StringBuilder();
                StringBuilder columnValueList = new StringBuilder();
                for (Column column : columnList)
                {

                    columnNameList.append(" " + column.getName() + ",");
                    if (column.getValue() instanceof String)
                    {
                        columnValueList.append(" '" + column.getValue() + "',");
                    } else if ("java.sql.Timestamp".equals(column.getType()))
                    {
                        columnValueList.append(" '" + new Timestamp((long) column.getValue()) + "',");
                    } else
                    {
                        columnValueList.append(" " + column.getValue() + ",");
                    }
                }
                while (columnNameList.charAt(columnNameList.length() - 1) == ',')
                {
                    columnNameList.deleteCharAt(columnNameList.length() - 1);
                }
                while (columnValueList.charAt(columnValueList.length() - 1) == ',')
                {
                    columnValueList.deleteCharAt(columnValueList.length() - 1);
                }

                String sql =
                        "insert into " + beforeImage.getTableName() + " (" + columnNameList.toString() + ")values(" + columnValueList
                                .toString() + ")";
                log.info("exe before image ={}", sql);
                Db.update(null, sql);
            }
        }
    }

    public void updateStatusFinish(Connection connection) throws SQLException
    {
        String sql = "update global_transaction_undo_log set status =1 where id = " + id;
        Db.update(connection, sql);

    }
}

